<script setup>
import { ref, computed, watch, defineModel } from 'vue'
import AI from './ai.js'
import { useMessage } from 'naive-ui'
// import MarkdownIt from 'markdown-it'
import Markdown from 'vue3-markdown-it';
import 'highlight.js/styles/monokai.css';
import Session from './session.js';
import { PlanetOutline, Planet, NewspaperOutline } from '@vicons/ionicons5';
import { NodeHandler } from './node.js'
import { Handle, Position, useVueFlow } from '@vue-flow/core'

// const {  nodes } = useVueFlow()
const nodes = defineModel('nodes', {default:[]});
// const md = new MarkdownIt()
const messager = useMessage()
const chatIds = defineModel("chatids", {
    default:[],
})
const sessionName = defineModel("session",{
    required:true
})
const sessionHander = new Session(sessionName.value)
const running = ref(false)
// const tasks = defineModel("tasks")
const chats = defineModel("chats")
const taskLabels = computed(()=>{
    let ks = []
    for(let node of nodes.value){
        ks.push({
            label:"@"+node.id,
            value:node.id,
        })
    }
    console.log("tasks:",ks)
    return ks
})
const inputTask = ref("");
const tasks = computed(()=>{
    let d = {}
    taskLabels.value = []
    for(let node of nodes.value){
        d[node.id] = node.value.data.value;
        taskLabels.value.push({
            label:"@"+node.id,
            value:node.id,
        })
    }
    return d
})

const useid = ref('');
const promptInputRef = ref(null);
const tags = ref([]);
const background = ref("");
watch(()=>tags.value,(newVal)=>{
    console.log("new tag:",newVal)
    console.log(nodes.value)
    background.value = "## 背景信息\n\n"
    let e = false
    for(let id of newVal){
        for(let node of nodes.value){
            if (node.id == id){
                e = true       
                background.value += JSON.stringify(node.data.value)
            }
        }
    }
    if (e){
        background.value = background.value.replace(/\!\[(.*?)\]\((.*?)\)/g, "");
        background.value+="\n"
    }else{
        background.value =""
    }
    
},{deep:true})

const aiArg = ref({
  name: "",
  type: "Agent",
  target: "",
  points: [],
  example: "",
  useJson: false,
  points: [],
  batch_size: 10,
  data: [],
  threads: 1,
  use_json: false,
  model:"DeepSeek-R1-Distill-Qwen-32B",
  model_api:"/v1/chat/completions",
})
var ai = null;
const searching = ref(false)
const llm_msg = (prmpt, on_end) =>{
  if (running.value){
    return
  }
  if( ai == null){
    ai = new AI(aiArg.value.model_api, aiArg.value.model);
    ai.use_default_params();
  }
  let ix = chats.value[useid.value].length-1
  running.value = true
  let thinking = false;
  prmpt = prmpt.replace(/\!\[(.*?)\]\((.*?)\)/g, "");
  ai.stream(prmpt, (data) => {
    let msg = data.choices[0].delta.content;
    if (msg.match(/<think>/g)){
        msg = msg.replace(/<think>/g, '');
        thinking = true;
    }else if (msg.match(/<\/think>/g)){
        msg = msg.replace(/<\/think>/g, '');
        thinking = false;
    }
    if (thinking){
        chats.value[useid.value][ix].think += msg;
    }else{
        chats.value[useid.value][ix].answer += msg;
    }
    
    // chats.value[useid.value][ix].render = md.render(chats.value[useid.value][ix].answer);
  }, (finlly) => {
    running.value = false
    on_end(finlly)
    sessionHander.save(chats.value, (data)=>{
        console.log("save session:",data)
    })
  })
}

const results = ref("");
const search = (query,each, end_callback) =>{
    if (query == ''){
        return;
    }
    searching.value = true
    let node = new NodeHandler("search", "curl")
    let back =  "45.77.165.99";
    let backapi =  "https://"+back + ":31111/v1/search";
    node.api = "https://149.28.136.153:31111/v1/search"
    if (net_active_deep.value){
        node.data = {data:{'name': 'ddg', 'query': query, "nodes":"45.76.19.160,207.148.0.35,139.84.169.87,45.63.62.168,45.77.165.99,64.176.225.130,149.28.136.153,45.76.186.26,149.28.26.192,207.148.92.5", "api":"http://115.236.8.156:15002/v1/qa"}}
    }else{
        node.data = {data:{'name': 'ddg', 'query': query}}
    }    
    node.use_stream = true
    node.outputArgs = ["items"]
    node.args = ["query"]
    node.headers = {
        "Content-Type": "application/json",
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
    }
    node.usepost = true
    let items = []
    results.value = "";
    let tryAgain = null
    
    node.stream(null, (each_data)=>{
        
        if (Array.isArray(each_data) && each_data[0] == null){
            tryAgain = setTimeout(()=>{
                if (tryAgain){
                    node.api = backapi
                    node.stream(null, (each_data)=>{
                        
                        
                        results.value += "\n"+  JSON.stringify(each_data, null, 2)
                        for(let item of each_data){
                            if (item != null){
                    
                                console.log("item:",item)
                                each(item);
                                items.push(item);
                            }
                        }
                    }, ()=>{
                        searching.value = false
                        end_callback(items)
                    })
                }
            },4000)
        }
        console.log("each_data:",each_data)
        results.value += "\n"+  JSON.stringify(each_data, null, 2)
        for(let item of each_data){
            if (item != null){
                console.log("item:",item)
                each(item);
                items.push(item);
            }
            
        }
    }, ()=>{
        if (items.length > 0){
            searching.value = false
            end_callback(items)
        }
        
    })

    

    

}

const prompt = ref('');
watch(()=>useid.value, () => {
    ai = new AI(aiArg.value.model_api, aiArg.value.model);
    ai.use_default_params();
    messager.success('切换到会话 ' + useid.value)
    let qas = chats.value[useid.value]
    if ( qas != null){
        for (let msg of qas){
            ai.history.push({
                role: "user",
                content: msg.question
            })
            ai.history.push({
                role: "assistant",
                content: msg.answer
            })
        }
    }
    
},{deep:true})

const historyPrompts = computed(() => {
    if (chats.value[useid.value] != null){
        return chats.value[useid.value].map((item) => {
            return {
                label: item.question,
                value: item.question,
            }
        })
    } else {
        return [];
    }
})
const showAITalk = ref(false);
const showTalk = ()=>{
    // showAITalk.value = !showAITalk.value;
    promptInputRef.value?.focus();
}
const promptName = ref("");
const addAnewChat = ()=>{
    chatIds.value.push(promptName.value);
    useid.value = promptName.value;
}
// const sendPromptNew = ()=>{
//     if (prompt.value.trim() == ""){
//         messager.error("不能为空")
//         return
//     }
//     sendPrompt();
// }

const sendPrompt = ()=>{
    if (prompt.value.trim() == ""){
        messager.error("不能为空")
        return
    }
    
    if (chatIds.value.length == 0){
        chatIds.value.push(prompt.value);
        useid.value = prompt.value;
    }
    let question = prompt.value;
    if (background.value.length > 0){
        question = background.value + question
    }
    if (chats.value[useid.value] == null){
        chats.value[useid.value] = [];
    }
    chats.value[useid.value].push({ 
        question: question, 
        answer:"",
        render: null,
        think:"",
        web:[],
    });
    setTimeout(() => {
        let ix = chats.value[useid.value].length-1
        
        if (net_active.value){
            let cs = {};
            search(question, (i)=>{
                if (i.title != ""){
                    if (cs[i.title] == null){
                        cs[i.title] = i
                        // chats.value[useid.value][ix].think += `${i.title} / ${i.url}\n` ;
                        chats.value[useid.value][ix].web.push(i);
                    }else{
                        
                    }
                    
                }
            },(items)=>{
                console.log("end net:",items);
                chats.value[useid.value][ix].web = items ;
                startTimer()
                let webs = `联网搜索结果如下，回答中请标注上引用来源(形式如：[2](https://xxx.xxx.xx))：                 `
                for (let i of items){
                    if (i.chunk != null){
                        webs += `\n(${i.title})[${i.url}]\n\t${i.chunk}`
                    }else{
                        webs += `\n\n(${i.title})[${i.url}]\n\t${i.desc}`
                    }
                    
                }
                question = webs +"\n" +question
                llm_msg(question, (data) =>{
                    console.log("end llm:",data);
                    prompt.value = "";
                    stopTimer();
                    chats.value[useid.value][ix].answer += "\n\n ---- \n\n ## 网络来源\n\n"
                    for (let ik in cs){
                        let i = cs[ik];
                        if (i.chunk != null){
                            chats.value[useid.value][ix].answer += `\n\n - [${i.title}](${i.url})` ;
                        }else{
                            chats.value[useid.value][ix].answer += `\n\n - [${i.title}](${i.url})` ;
                        }
                    }
                    console.log("all:\n",chats.value[useid.value][ix].answer) 
                });
            })
        }else{
            startTimer()
            llm_msg(question, (data) =>{
                console.log("end normal:",data);
                prompt.value = "";
                stopTimer();
            });
        }
        
    }, 500);
    

}
const seconds = ref(0);
const milliseconds = ref(0);
const timerId = ref(null);
const net_active = ref(false);
const net_active_deep = ref(false);
const goto = (page) =>{
    // click to jumtp to web by new tab
    window.open(page.url, 'newWindow', 'width=500,height=800,left=-10,top=200');
}
let totalTime = 0;

const updateTimer = () => {
  totalTime++;
  seconds.value = Math.floor(totalTime / 1000);
  milliseconds.value = totalTime % 1000;
};
const startTimer = () => {
  totalTime = 0;
  if (!timerId.value) {
    timerId.value = setInterval(updateTimer, 1);
  }
};
const stopTimer = () => {
  if (timerId.value) {
    clearInterval(timerId.value);
    timerId.value = null;
  }
};

const del_session = ()=>{
    console.log("del:",useid.value)
    sessionHander.del(chats.value,useid.value,()=>{
        chatIds.value = chatIds.value.filter(i => i != useid.value);
    })
}
</script>

<template>

    <n-layout has-sider style="height: 100vh; margin-top:0px;padding-top:0px" v-shortkey="['ctrl', '/']" @shortkey="showTalk()" > 
        <n-layout-sider style="margin-top:10px" v-if="chatIds.length > 0" >
            <n-space vertical>
                <n-space vertical v-for="id in chatIds">
                    <n-h3 prefix="bar" type="primary" v-if="id == useid"  >
                        <n-space >
                            <n-button style="width:210px" type="success"   @click="useid = id"   > {{ id }}</n-button>
                            <n-button @click="del_session">x</n-button>
                        </n-space>
                        
                    </n-h3>
                    <n-h3 prefix="bar" type="info"     v-else  >
                        
                        <n-button  @click="useid = id" secondary  style="width:100%"  >{{ id }}</n-button>
                    </n-h3>
                </n-space>
                <n-input
                    v-model:value="promptName"
                    style="width: 88vw;"
                    placeholder="新问题"
                    clearable
                    @keyup.enter="addAnewChat"
                />

            </n-space>
        </n-layout-sider>
        <n-layout-content>
            <n-space vertical style="margin:10px;overflow-y:auto;height: 91vh;">
                <n-space v-if="useid != ''" vertical v-for="chat in chats[useid]">
                    <n-flex justify="end">
                        <n-ellipsis style="max-width: 640px;text-align: justify;"   expand-trigger="click" line-clamp="4" :tooltip="false">
                            <n-text type="info">{{ chat.question  }}</n-text>
                        </n-ellipsis>
                    </n-flex>
                    <n-flex justify="start">
                        <n-spin :show="searching">
                        <n-space vertical style="width:71vw;border-radius: 8px;border: 1px solid gray;margin: 0px;padding: 13px;text-align: justify;" >
                            
                               
                            
                            <n-collapse :default-expanded-names="['thinking']" >
                                <n-collapse-item name="searching">
                                    <n-space>
                                        <n-tag size="small" style="cursor:pointer; margin-left:1px; margin-top:1px"  round :bordered="false" type="success" v-for="page in chat.web" @click="goto(page)" >   
                                            <template #icon>
                                                <n-icon :component="NewspaperOutline" />
                                            </template>
                                            {{ page.title }}
                                        </n-tag>                                    
                                    </n-space>
                                    <template #header>
                                        <n-gradient-text v-if="searching == true" type="primary" :size=12 :gradient="{from: '#70c0e8',to: '#63e2b7'}" >搜索中</n-gradient-text>
                                        <n-gradient-text v-else type="primary" :size=12 :gradient="{from: '#70c0e8',to: '#63e2b7'}" >搜索结果:{{ chat.web.length }}</n-gradient-text>
                                    </template>
                                </n-collapse-item>
                                <n-collapse-item name="thinking" :show="true">
                                    <n-h3 prefix="bar">
                                        <n-gradient-text type="info" :size=12 :gradient="{from: '#70c0e8',to: '#63e2b7'}" >
                                            <pre style="text-wrap: auto;word-break: break-all;">{{ chat.think.trim()}}</pre>
                                        </n-gradient-text>
                                    </n-h3>
                                    
                                    <template #header>
                                        <n-gradient-text v-if="searching == true" type="info" :size=12 :gradient="{from: '#70c0e8',to: '#63e2b7'}" >搜索中... </n-gradient-text>
                                        <n-gradient-text v-else type="info" :size=12 :gradient="{from: '#70c0e8',to: '#63e2b7'}" >思考中... (点击显示思考过程)</n-gradient-text>
                                    </template>
                                    <template #header-extra>
                                        <n-text type="success">
                                            思考长度：{{ chat.think.length  }} 用时: {{ seconds }}.{{ milliseconds }}s
                                        </n-text>
                                    </template>
                                </n-collapse-item>
                            </n-collapse>
                            
                                <Markdown :source="chat.answer"></Markdown>
                            
                        </n-space>
                        </n-spin>
                      

                    </n-flex>
                    
                </n-space>
                <n-flex justify="end"  >
                    <n-spin :show="running || searching">
                        <n-flex style="width: 70.3vw;border-radius: 8px;border: 1px solid #63e2b7;margin: 0px;padding: 13px;text-align: justify;" vertical>
                            <n-flex>
                                <n-switch v-model:value="net_active" size="small">
                                    <template #checked>
                                        联网搜索模式
                                    </template>
                                    <template #checked-icon>
                                        <n-icon :component="Planet" />
                                    </template>
                                    <template #unchecked>
                                        不联网模式
                                    </template>
                                    <template #unchecked-icon>
                                        <n-icon :component="PlanetOutline" />
                                    </template>
                                </n-switch>
                                <n-switch  v-if="net_active" v-model:value="net_active_deep" size="small">
                                    <template #checked>
                                        读取每一个网页
                                    </template>
                                    <template #checked-icon>
                                        <n-icon :component="Planet" />
                                    </template>
                                    <template #unchecked>
                                        只读取搜索页面
                                    </template>
                                    <template #unchecked-icon>
                                        <n-icon :component="PlanetOutline" />
                                    </template>
                                </n-switch>
                                <n-dynamic-tags v-model:value="tags" :max="3" size="small" type="info">
                                    <template #input="{ submit, deactivate }">
                                        <n-auto-complete
                                            
                                            v-model:value="inputTask"
                                            size="tiny"
                                            :options="taskLabels"
                                            placeholder="任务数据集"
                                            :clear-after-select="true"
                                            @select="submit($event)"
                                            @blur="deactivate"
                                        />
                                    </template>
                                    <template #trigger="{ activate, disabled }">
                                        <n-button
                                            size="tiny"
                                            type="primary"
                                            dashed
                                            :disabled="disabled"
                                            @click="activate()"
                                        >
                                            <template #icon>
                                            <n-icon>
                                                <Add />
                                            </n-icon>
                                            </template>
                                            使用任务模式中的数据
                                        </n-button>
                                    </template>
                                </n-dynamic-tags>
                            </n-flex>
                            <n-input-group>
                                <!-- <n-button type="primary" style="height:100%" @click="">联网</n-button> -->
                                <n-input
                                    
                                    v-model:value="prompt"
                                    type="textarea"
                                    ref="promptInputRef" 
                                    style=""
                                    :autosize="{
                                        minRows: 3,
                                    }"
                                    placeholder="@Ctrl+/ 唤出对话框 | Ctrl + Enter 发送"
                                    clearable
                                    
                                />
                                
                            </n-input-group>
                            <n-button @click="sendPrompt">发送 <n-text v-if="background.length > 0" type="success" >/背景:{{ background.length }}字</n-text></n-button>

                        </n-flex>
                    </n-spin>
                </n-flex>
                
            </n-space>
        </n-layout-content>
    </n-layout>
    

</template>


<style >
a{
    color: #63e2b7;
    border: 0.1px solid;
    font-size: xx-small;
    text-decoration: solid;
    border-radius: 8px;
    padding-left: 3px;
    padding-right: 3px;
    padding-top: 1px;
    padding-bottom: 1px;
}
</style>